/******************************************************************************* * Copyright (c) 2014 antoniomariasanchez at gmail.com. All rights reserved. This program and the accompanying materials * are made available under the terms of the GNU Public License v3.0 which accompanies this distribution, and is * available at http://www.gnu.org/licenses/gpl.html * * Contributors: antoniomaria - initial API and implementation ******************************************************************************/ package net.sf.gazpachoquest.jaas; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.net.ConnectException; import java.net.HttpURLConnection; import java.net.URL; import java.net.URLEncoder; import java.util.Map; import javax.json.Json; import javax.json.JsonArray; import javax.json.JsonNumber; import javax.json.JsonObject; import javax.json.JsonReader; import javax.security.auth.Subject; import javax.security.auth.callback.Callback; import javax.security.auth.callback.CallbackHandler; import javax.security.auth.callback.NameCallback; import javax.security.auth.callback.PasswordCallback; import javax.security.auth.login.AccountException; import javax.security.auth.login.FailedLoginException; import javax.security.auth.login.LoginException; import javax.security.auth.spi.LoginModule; import net.sf.gazpachoquest.jaas.auth.Account; import net.sf.gazpachoquest.jaas.auth.RespondentAccount; import net.sf.gazpachoquest.jaas.auth.RoleAccount; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class RespondentsLoginModule implements LoginModule { private static Logger logger = LoggerFactory.getLogger(RespondentsLoginModule.class); private CallbackHandler handler; private Subject subject; private Account userPrincipal; private String endpoint; @Override public void initialize(Subject subject, CallbackHandler callbackHandler, Map<String, ?> sharedState, Map<String, ?> options) { endpoint = (String) options.get("gazpachoquest.rest.endpoint"); handler = callbackHandler; this.subject = subject; } @Override public boolean login() throws LoginException { Callback[] callbacks = new Callback[2]; callbacks[0] = new NameCallback("username"); callbacks[1] = new PasswordCallback("password", true); try { handler.handle(callbacks); String username = ((NameCallback) callbacks[0]).getName(); String password = String.valueOf(((PasswordCallback) callbacks[1]).getPassword()); logger.info("New username attempt for user: {}", username); userPrincipal = doLogin(password); logger.info("Access granted to user {}", userPrincipal.getFullName()); return true; } catch (LoginException e) { throw e; } catch (Exception e) { logger.error(e.getMessage(), e); throw new FailedLoginException("An unknown error has occurred in authentication process"); } } public Account doLogin(String invitation) throws IOException, LoginException { RespondentAccount account = new RespondentAccount(); String query = String.format("invitation=%s", URLEncoder.encode(invitation, "UTF-8")); URL url = new URL(endpoint + "/auth?" + query); HttpURLConnection connection = (HttpURLConnection) url.openConnection(); try (InputStream is = connection.getInputStream(); JsonReader rdr = Json.createReader(is)) { JsonObject obj = rdr.readObject(); String givenNames = obj.getString("givenNames"); String surname = obj.getString("surname"); String email = obj.getString("email"); String apiKey = obj.getString("apiKey"); String secret = obj.getString("secret"); String preferredLanguage = obj.getString("preferredLanguage"); account.setGivenNames(givenNames); account.setSurname(surname); account.setEmail(email); account.setApiKey(apiKey); account.setSecret(secret); account.setPreferredLanguage(preferredLanguage); JsonArray roles = obj.getJsonArray("roles"); for (JsonObject role : roles.getValuesAs(JsonObject.class)) { String roleName = role.getString("name"); account.assingRole(roleName); } JsonArray grantedquestionnaireIds = obj.getJsonArray("grantedquestionnaireIds"); for (JsonNumber grantedquestionnaireId : grantedquestionnaireIds.getValuesAs(JsonNumber.class)) { Integer questionnaireId = grantedquestionnaireId.intValue(); account.grantquestionnaireId(questionnaireId); } } catch (ConnectException e) { logger.error(e.getMessage(), e); throw new FailedLoginException(String.format("Server {} is temporarily unavailable", endpoint)); } catch (FileNotFoundException e) { // 404 error logger.error(e.getMessage(), e); throw new FailedLoginException(String.format("Configuration error. Resource {} not found", url)); } catch (IOException e) { String message = getErrorMessage(e, connection); logger.error(e.getMessage(), e); throw new AccountException(message); } return account; } private String getErrorMessage(IOException exception, HttpURLConnection connection) { String message = exception.getMessage(); try (InputStream error = connection.getErrorStream(); JsonReader rdr = Json.createReader(error)) { JsonObject obj = rdr.readObject(); message = obj.getString("message"); } catch (Exception e) { // ignore. likely there is no response } return message; } @Override public boolean commit() throws LoginException { subject.getPrincipals().add(userPrincipal); for (RoleAccount role : userPrincipal.getRoles()) { subject.getPrincipals().add(role); } return true; } @Override public boolean abort() throws LoginException { return false; } @Override public boolean logout() throws LoginException { subject.getPrincipals().remove(userPrincipal); for (RoleAccount role : userPrincipal.getRoles()) { subject.getPrincipals().remove(role); } return true; } }